// Copyright (c) 2011 Blue Onion Software, All rights reserved
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Hosting;
using System.Web.Security;
using BlueOnionSoftware.Bloget.Providers.Data;

namespace BlueOnionSoftware.Bloget.Providers
{
    public class XmlRoleProvider : RoleProvider
    {
        private string _dataStorePath;
        private string _applicationName;

        public string DataStorePath
        {
            get { return _dataStorePath; }
            set { _dataStorePath = value; }
        }

        private string DataStoreFile
        {
            get
            {
                string path;

                if (HttpContext.Current != null)
                {
                    path = HttpContext.Current.Server.MapPath(DataStorePath);
                }

                else
                {
                    path = HostingEnvironment.ApplicationPhysicalPath;
                    var dataStorePath = DataStorePath.Replace("~/", string.Empty);
                    path = Path.Combine(path, dataStorePath);
                }

                return Path.Combine(path, string.Format("{0}_Roles.xml", _applicationName));
            }
        }

        public override void Initialize(string name, NameValueCollection config)
        {
            Throw.IfNull(config, "config");

            if (String.IsNullOrEmpty(name))
            {
                name = "XmlRoleProvider";
            }

            base.Initialize(name, config);

            _dataStorePath = String.IsNullOrEmpty(config["dataStorePath"]) ? "~/App_Data" : config["dataStorePath"];
            _applicationName = String.IsNullOrEmpty(config["applicationName"]) ? "Bloget" : config["applicationName"];
        }

        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            var roles = RoleCollection.Load(DataStoreFile);

            foreach (var roleName in roleNames)
            {
                var role = roles.FindByName(roleName);

                if (role != null)
                {
                    foreach (var username in usernames)
                    {
                        if (!role.Users.Contains(username))
                        {
                            role.Users.Add(username);
                        }
                    }
                }
            }

            roles.Save(DataStoreFile);
        }

        public override string ApplicationName
        {
            get { return _applicationName; }
            set { _applicationName = value; }
        }

        public override void CreateRole(string roleName)
        {
            var roles = RoleCollection.Load(DataStoreFile);
            var role = new Role {Name = roleName};
            roles.Add(role);
            roles.Save(DataStoreFile);
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            var roles = RoleCollection.Load(DataStoreFile);
            var role = roles.FindByName(roleName);

            if (role != null)
            {
                if (throwOnPopulatedRole && role.Users.Count > 0)
                {
                    return false;
                }

                roles.Remove(role);
                roles.Save(DataStoreFile);
            }

            return true;
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            var roles = RoleCollection.Load(DataStoreFile);
            var role = roles.FindByName(roleName);
            var users = new List<string>();

            if (role != null)
            {
                users.AddRange(role.Users.Where(user => user.Contains(usernameToMatch)));
            }

            return users.ToArray();
        }

        public override string[] GetAllRoles()
        {
            var roles = RoleCollection.Load(DataStoreFile);
            var roleList = new List<Role>(roles);

            var names = roleList.ConvertAll(ConvertRoleToString);
            return names.ToArray();
        }

        private static string ConvertRoleToString(Role role)
        {
            return role.Name;
        }

        public override string[] GetRolesForUser(string username)
        {
            var roles = RoleCollection.Load(DataStoreFile);
            return (roles.Where(role => role.Users.Contains(username)).Select(role => role.Name)).ToArray();
        }

        public override string[] GetUsersInRole(string roleName)
        {
            var roles = RoleCollection.Load(DataStoreFile);
            var role = roles.FindByName(roleName);
            string[] output = null;

            if (role != null)
            {
                output = new string[role.Users.Count];
                role.Users.CopyTo(output, 0);
            }

            return output;
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            var roles = RoleCollection.Load(DataStoreFile);
            var role = roles.FindByName(roleName);

            if (role != null)
            {
                return role.Users.Contains(username);
            }

            return false;
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            var roles = RoleCollection.Load(DataStoreFile);

            foreach (var roleName in roleNames)
            {
                var role = roles.FindByName(roleName);

                if (role != null)
                {
                    foreach (var username in usernames)
                    {
                        if (role.Users.Contains(username))
                        {
                            role.Users.Remove(username);
                        }
                    }
                }
            }

            roles.Save(DataStoreFile);
        }

        public override bool RoleExists(string roleName)
        {
            var roles = RoleCollection.Load(DataStoreFile);
            var role = roles.FindByName(roleName);
            return role != null;
        }
    }
}